#include "pch.h"

#include "iosystem/binarywriter.h"



namespace IO
{

	RE_ImplementClass('BINW', IO::BinaryWriter, IO::StreamWriter);



	///
	BinaryWriter::BinaryWriter():
	m_enableMapping(false),
		m_isMapped(false),
		m_mapCursor(0),
		m_mapEnd(0)
	{

	}


	///
	BinaryWriter::~BinaryWriter()
	{
		if(this->IsOpen())
			this->Close();
	}





	///
	bool BinaryWriter::Open()
	{
		if(StreamWriter::Open())
		{
			if(this->m_enableMapping && this->m_stream->CanMap())
			{
				this->m_isMapped = true;

				this->m_mapCursor = (unsigned char*)this->m_stream->Map();

				this->m_mapEnd = this->m_mapCursor + this->m_stream->GetSize();
			}
			else
			{
				this->m_isMapped = false;

				this->m_mapCursor = 0;

				this->m_mapEnd = 0;
			}

			return true;
		}

		return false;
	}


	///
	void BinaryWriter::Close()
	{
		StreamWriter::Close();

		this->m_isMapped = false;

		this->m_mapCursor = 0;

		this->m_mapEnd = 0;
	}





	///
	void BinaryWriter::WriteChar(char c)
	{
		if(this->m_isMapped)
		{
			*this->m_mapCursor++ = c;
		}
		else
		{
			this->m_stream->Write(&c, sizeof(c));
		}
	}


	///
	void BinaryWriter::WriteUChar(uchar c)
	{
		if(this->m_isMapped)
		{
			*this->m_mapCursor++ = c;
		}
		else
		{
			this->m_stream->Write(&c, sizeof(c));
		}
	}


	///
	void BinaryWriter::WriteShort(short s)
	{
		s = this->m_byteOrder.Convert<short>(s);

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&s, this->m_mapCursor, sizeof(s));

			this->m_mapCursor += sizeof(s);
		}
		else
		{
			this->m_stream->Write(&s, sizeof(s));
		}
	}


	///
	void BinaryWriter::WriteUShort(ushort s)
	{
		s = this->m_byteOrder.Convert<ushort>(s);

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&s, this->m_mapCursor, sizeof(s));

			this->m_mapCursor += sizeof(s);
		}
		else
		{
			this->m_stream->Write(&s, sizeof(s));
		}
	}


	///
	void BinaryWriter::WriteInt(int i)
	{
		i = this->m_byteOrder.Convert<int>(i);

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&i, this->m_mapCursor, sizeof(i));

			this->m_mapCursor += sizeof(i);
		}
		else
		{
			this->m_stream->Write(&i, sizeof(i));
		}
	}


	///
	void BinaryWriter::WriteUInt(uint i)
	{
		i = this->m_byteOrder.Convert<uint>(i);

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&i, this->m_mapCursor, sizeof(i));

			this->m_mapCursor += sizeof(i);
		}
		else
		{
			this->m_stream->Write(&i, sizeof(i));
		}
	}


	///
	void BinaryWriter::WriteFloat(float f)
	{
		f = this->m_byteOrder.Convert<float>(f);

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&f, this->m_mapCursor, sizeof(f));

			this->m_mapCursor += sizeof(f);
		}
		else
		{
			this->m_stream->Write(&f, sizeof(f));
		}
	}


	///
	void BinaryWriter::WriteDouble(double d)
	{
		d = this->m_byteOrder.Convert<double>(d);

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&d, this->m_mapCursor, sizeof(d));

			this->m_mapCursor += sizeof(d);
		}
		else
		{
			this->m_stream->Write(&d, sizeof(d));
		}
	}


	///
	void BinaryWriter::WriteBool(bool b)
	{
		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&b, this->m_mapCursor, sizeof(b));

			this->m_mapCursor += sizeof(b);
		}
		else
		{
			this->m_stream->Write(&b, sizeof(b));
		}
	}


	///
	void BinaryWriter::WriteString(const Helper::String &str)
	{
		this->WriteUShort(ushort(str.Length()));

		if(str.Length() > 0)
		{
			if(this->m_isMapped)
			{
				Memory::MemoryManager::Copy(str.GetAsCharPtr(), this->m_mapCursor, str.Length());

				this->m_mapCursor += str.Length();
			}
			else
			{
				this->m_stream->Write((void*)str.GetAsCharPtr(), str.Length());
			}
		}
	}


	///
	void BinaryWriter::WriteFloat2(Math::float2 f2)
	{
		Math::float2 val(this->m_byteOrder.Convert<float>(f2.getx()),
			this->m_byteOrder.Convert<float>(f2.gety()));

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&val, this->m_mapCursor, sizeof(val));

			this->m_mapCursor += sizeof(val);
		}
		else
		{
			this->m_stream->Write(&val, sizeof(val));
		}
	}


	///
	void BinaryWriter::WriteFloat4(const Math::float4 &f)
	{
		Math::float4 val = f;

		this->m_byteOrder.ConvertInPlace<Math::float4>(val);

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&val, this->m_mapCursor, sizeof(val));

			this->m_mapCursor += sizeof(val);
		}
		else
		{
			this->m_stream->Write(&val, sizeof(val));
		}
	}


	///
	void BinaryWriter::WritePoint2(const Math::point2 & v)
	{
		float val[2];

		val[0] = this->m_byteOrder.Convert<float>(v.getx());
		val[1] = this->m_byteOrder.Convert<float>(v.gety());

		const tSize writeSize = sizeof(float) * 2;

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(val, this->m_mapCursor, writeSize);

			this->m_mapCursor += writeSize;
		}
		else
		{
			this->m_stream->Write(val, writeSize);
		}
	}


	///
	void BinaryWriter::WritePoint4(const Math::point4 &p)
	{
		float val[3];

		val[0] = this->m_byteOrder.Convert<float>(p.getx());
		val[1] = this->m_byteOrder.Convert<float>(p.gety());
		val[2] = this->m_byteOrder.Convert<float>(p.getz());

		const tSize writeSize = sizeof(float) * 3;

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(val, this->m_mapCursor, writeSize);

			this->m_mapCursor += writeSize;
		}
		else
		{
			this->m_stream->Write(val, writeSize);
		}
	}


	///
	void BinaryWriter::WritePlane(const Math::plane &p)
	{
		float val[4];

		val[0] = this->m_byteOrder.Convert<float>(p.getA());
		val[1] = this->m_byteOrder.Convert<float>(p.getB());
		val[2] = this->m_byteOrder.Convert<float>(p.getC());
		val[3] = this->m_byteOrder.Convert<float>(p.getD());

		const tSize writeSize = sizeof(float) * 4;

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(val, this->m_mapCursor, writeSize);

			this->m_mapCursor += writeSize;
		}
		else
		{
			this->m_stream->Write(val, writeSize);
		}
	}


	///
	void BinaryWriter::WriteVector2(const Math::vector2 &v)
	{
		float val[2];

		val[0] = this->m_byteOrder.Convert<float>(v.getx());
		val[1] = this->m_byteOrder.Convert<float>(v.gety());

		const tSize writeSize = sizeof(float) * 2;

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(val, this->m_mapCursor, writeSize);

			this->m_mapCursor += writeSize;
		}
		else
		{
			this->m_stream->Write(val, writeSize);
		}
	}


	///
	void BinaryWriter::WriteVector3(const Math::vector3 &v)
	{
		float val[3];

		val[0] = this->m_byteOrder.Convert<float>(v.getx());
		val[1] = this->m_byteOrder.Convert<float>(v.gety());
		val[2] = this->m_byteOrder.Convert<float>(v.getz());

		const tSize writeSize = sizeof(float) * 3;

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(val, this->m_mapCursor, writeSize);

			this->m_mapCursor += writeSize;
		}
		else
		{
			this->m_stream->Write(val, writeSize);
		}
	}


	///
	void BinaryWriter::WriteVector4(const Math::vector4 & v)
	{
		float val[4];

		val[0] = this->m_byteOrder.Convert<float>(v.getx());
		val[1] = this->m_byteOrder.Convert<float>(v.gety());
		val[2] = this->m_byteOrder.Convert<float>(v.getz());
		val[3] = this->m_byteOrder.Convert<float>(v.getw());

		const tSize writeSize = sizeof(float) * 4;

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(val, this->m_mapCursor, writeSize);

			this->m_mapCursor += writeSize;
		}
		else
		{
			this->m_stream->Write(val, writeSize);
		}
	}


	///
	void BinaryWriter::WriteMatrix(const Math::matrix &m)
	{
		Math::matrix val = m;
		this->m_byteOrder.ConvertInPlace<Math::matrix>(val);

		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(&val, this->m_mapCursor, sizeof(val));

			this->m_mapCursor += sizeof(val);
		}
		else
		{
			this->m_stream->Write(&val, sizeof(val));
		}
	}





	///
	void BinaryWriter::SetMemoryMappingEnabled(bool b)
	{
		this->m_enableMapping = b;
	}


	///
	bool BinaryWriter::IsMemoryMappingEnabled() const
	{
		return this->m_enableMapping;
	}


	///
	void BinaryWriter::SetStreamEndianess(System::ByteOrder::Type order)
	{
		this->m_byteOrder.SetToEndianess(order);
	}


	///
	System::ByteOrder::Type BinaryWriter::GetStreamEndianess() const
	{
		return this->m_byteOrder.GetToEndianess();
	}


	///
	void BinaryWriter::WriteRawData(const void * ptr, tSize bytes)
	{
		if(this->m_isMapped)
		{
			Memory::MemoryManager::Copy(ptr, this->m_mapCursor, bytes);

			this->m_mapCursor += bytes;
		}
		else
		{
			this->m_stream->Write(ptr, bytes);
		}
	}

}